package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.common.handle;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;


import com.alibaba.fastjson.JSONObject;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ModelCategoryEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ProcessInstanceStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.SpringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.StringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ResponseResult;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.exception.EventCallSuspendException;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowProcessInstanceOperateTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.User;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.*;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.common.exception.BusiException;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.utils.HttpUtils;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.common.api.FlowableException;
import org.flowable.engine.repository.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;


@ControllerAdvice
@ResponseBody
public class WebExceptionHandle {
    private static Logger logger = LoggerFactory.getLogger(WebExceptionHandle.class);

    /**
     * 400 - Bad Request
     */
    @ExceptionHandler(Exception.class)
    public ResponseResult<Object> handleHttpException(Exception e, HttpServletRequest request) {
        logger.error("handleHttpException:", e);
        addExceptionLog(e, request);

        FlowTaskService flowTaskService = SpringUtils.getBean(FlowTaskService.class);
        flowTaskService.flowDirector(HttpUtils.getRequsetParam(request));

        if (e instanceof BusiException) {
            BusiException be = (BusiException) e;
            return ResponseResult.fail(be.getErrorCode().errCode(), be.getErrorCode().errMessage());
        }
        if (e instanceof FlowableException) {
            String errMsg = EventCallSuspendException.getErrMsg();
            if (Objects.nonNull(e.getCause())) {
                errMsg = e.getCause().getMessage();
            }
            EventCallSuspendException.clearErrMsg();
            return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), errMsg != null ? errMsg : "流程流转异常");
        }

        //参数校验失败
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            FieldError fieldError = exception.getBindingResult().getFieldError();
            if (Objects.nonNull(fieldError)) {
                return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), fieldError.getDefaultMessage());
            }
        }
        return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), ErrorCode.BAD_REQUEST.errMessage());
    }

    @Async
    public void addExceptionLog(Exception e, HttpServletRequest request) {
        List<StackTraceElement> traceElementList = Arrays.stream(e.getStackTrace())
                .filter(stackTraceElement -> stackTraceElement.getClassName().contains("iwhalecloud")).collect(Collectors.toList());
        if (traceElementList.size() > 0){
            StackTraceElement stackTraceElement = traceElementList.get(0);
            String operationMethod = stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName();

            JSONObject response = new JSONObject();
            response.put("ErrorMethod", operationMethod + ":" + stackTraceElement.getLineNumber());
            response.put("ErrorMessage", e.getCause() != null ? e.getCause().getMessage() : e.getMessage());

            JSONObject requsetParam = HttpUtils.getRequsetParam(request);
            String deploymentId = requsetParam.getString("deploymentId");
            String processInstanceId = requsetParam.getString("processInstanceId");
            String taskId = requsetParam.getString("workItemId");
            taskId = StringUtils.isNotEmpty(taskId) ? taskId : requsetParam.getString("taskId");
            if (StringUtils.isBlank(processInstanceId) && StringUtils.isNotBlank(taskId)) {
                TaskService taskService = SpringUtils.getBean(TaskService.class);
                processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
            }
            if (StringUtils.isNotBlank(processInstanceId)) {
                RuntimeService runtimeService = SpringUtils.getBean(RuntimeService.class);
                deploymentId = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult().getDeploymentId();
                // 流程实例ID存在 更新流程实例状态
                FlowProcessInstanceTraceRepository flowProcessInstanceTraceRepository = SpringUtils.getBean(FlowProcessInstanceTraceRepository.class);
                flowProcessInstanceTraceRepository.updateStatusToError(processInstanceId);
                // 流程实例操作记录表增加异常信息记录
                FlowProcessInstanceOperateTraceRepository flowProcessInstanceOperateTraceRepository = SpringUtils.getBean(FlowProcessInstanceOperateTraceRepository.class);
                User user = SpringUtils.getBean(UserRepository.class).getCurrentUser();
                if(user != null){
                    FlowProcessInstanceOperateTraceVo operateTrace = new FlowProcessInstanceOperateTraceVo(ProcessInstanceStatusEnum.ERROR.getCode(), processInstanceId, taskId,
                            user.getUserId(), user.getUserName(), response.toString());
                    flowProcessInstanceOperateTraceRepository.insertProcessInstanceOperateTrace(operateTrace);
                }
            }
            Model model = null;
            if (StringUtils.isNotBlank(deploymentId)) {
                RepositoryService repositoryService = SpringUtils.getBean(RepositoryService.class);
                model = repositoryService.createModelQuery().deploymentId(deploymentId).singleResult();
            }
            FlowModelRepository flowModelRepository = SpringUtils.getBean(FlowModelRepository.class);
            flowModelRepository.commonRuleActionAuditLog(model, ProcessInstanceStatusEnum.ERROR.getDesc(), model != null ? ModelCategoryEnum.getDescritionByCode(model.getCategory()) : null, "",
                    requsetParam.toString(), operationMethod, request.getMethod(), request.getServletPath(), "失败", response.toString());
        }
    }

}
